import * as THREE from 'three';
import BaseModel from '../BaseModel';
import { loadByUrl } from '../tools/loader';
import ResourceTracker from '../tools/ResourceTracker';

/**
 * @description 获取作物模型的二维插入点坐标
 */
function getCropCoordinate(params: {
  /** 中心xz平面坐标 */
  center: [number, number];
  /** x方向mesh长度 */
  xLength: number;
  /** z方向msh长度 */
  zLength: number;
  /** x方向间距  默认:Math.floor(xLength / 2)*/
  xSpacing?: number;
  /** z方向间距  默认:Math.floor(zLength / 10)*/
  zSpacing?: number;
}): Array<{ x: number; y: number }> {
  const { center, xLength, zLength, xSpacing = Math.floor(xLength / 2), zSpacing = Math.floor(zLength / 10) } = params;

  const getOffset = (leftBound: number, rightBound: number, spacing: number) => {
    let end = leftBound - spacing / 2;

    for (; end + spacing <= rightBound; end += spacing) {}

    const margin = (spacing / 2 + rightBound - end) / 2;
    return (margin < spacing / 2 ? -1 : 1) * Math.abs(margin - spacing / 2);
  };

  const _getCropCoordinate = (firstPoint: [number, number], thirdPoint: [number, number], xSpacing: number, zSpacing: number) => {
    const [x1, y1] = firstPoint;
    const [x2, y2] = thirdPoint;

    const xMargin = getOffset(x1, x2, xSpacing);
    const yMargin = getOffset(y1, y2, zSpacing);

    const startX = x1 - xSpacing / 2;
    const startY = y1 - zSpacing / 2;

    const cropCoordinate = [];
    for (let x = startX + xSpacing; x <= x2; x += xSpacing) {
      for (let y = startY + zSpacing; y <= y2; y += zSpacing) {
        cropCoordinate.push({
          x: x + xMargin,
          y: y + yMargin,
        });
      }
    }

    if (cropCoordinate.length === 0) {
      return [[(x1 + x2) / 2, (y1 + y2) / 2]];
    }

    return cropCoordinate;
  };

  const [x, z] = center;
  // 矩形的左上角
  const firstPoint = [x - xLength / 2, z - zLength / 2] as [number, number];
  // 矩形的右下角
  const thirdPoint = [x + xLength / 2, z + zLength / 2] as [number, number];
  return _getCropCoordinate(firstPoint, thirdPoint, xSpacing, zSpacing);
}

interface CropRackOption extends Editor.BaseModelOption {
  /** 父级group */
  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];
}

export default class CropRack extends BaseModel {
  /** name为 tu001 tu002 tu003 的mesh可挂载crop模型 */
  static cropMeshNameStartWith = 'tu';

  /** 作物模型数组 */
  cropObjects: Array<THREE.Object3D> = [];

  group = new THREE.Group();

  /** FIXME: 这里先用模拟写法 */
  id: number | string = Date.now();

  controls: Editor.Controls;
  // scene: Editor.BaseModelOption['scene'];
  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];

  cropResourceTracker = new ResourceTracker();

  override init(option?: Editor.BaseModelOption): Promise<THREE.Object3D | null> {
    return new Promise((resolve, reject) => {
      const { url, scale = this.scale } = option;
      this.scale = Object.assign(this.scale, scale);
      loadByUrl(url)
        .then((model) => {
          this.resourceTracker = new ResourceTracker();
          this.resourceTracker.track(model);
          this.model = model;
          this.changeScale(this.scale);
          // TEST: 当前挂载到自己的group下
          this.group.name = `cropRack-${this.getId()}`;
          this.group.add(model);
          this.cropStaffGroup.add(this.group);
          console.log('[cropRack]模型加载成功', this.getInfo());
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, model);
          resolve(model);
        })
        .catch((e) => {
          console.error('[cropRack]模型加载错误', e);
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, null);
          reject(null);
        });
    });
  }

  constructor(option: CropRackOption) {
    const { ...baseModelOption } = option;
    super(baseModelOption);
    // this.scene = baseModelOption.scene;
    this.cropStaffGroup = option.cropStaffGroup;
  }

  /** TODO: 完成该handler */
  controlsChangeHandler = (controls: Editor.Controls) => {
    const { transformControls, orbitControls } = controls;
    // 计算texture
    this._computeTexture(transformControls);
    // 视角跟随
  };

  private _computeTexture(transformControls: Editor.Controls['transformControls']) {
    if (transformControls.mode !== 'scale') return;
    // TODO:贴图repeat计算
    console.log('执行贴图计算');
  }

  private _renderCrops(cropModel: THREE.Object3D) {
    let counter = 0;
    this.model.traverse((mesh) => {
      if (mesh.name && mesh.name.startsWith(CropRack.cropMeshNameStartWith)) {
        let newModel = null;
        // 第一个模型复用，其余模型克隆
        counter === 0 ? (newModel = cropModel) : (newModel = cropModel.clone());
        const box = new THREE.Box3().setFromObject(mesh);
        const meshCenter = box.getCenter(new THREE.Vector3());
        const meshVec2Center = [meshCenter.x, meshCenter.z] as [number, number];
        const meshSize = box.getSize(new THREE.Vector3());

        const cropVec2points = getCropCoordinate({
          center: meshVec2Center,
          xLength: meshSize.x,
          zLength: meshSize.z,
        });

        cropVec2points.forEach((vec2Point) => {
          const { x: xPos, y: zPos } = vec2Point;
          // 位置修正
          const basePoint = this.cropStaffGroup.position.clone();
          const fixedVec3Point = new THREE.Vector3(xPos - basePoint.x, meshCenter.y - basePoint.y, zPos - basePoint.z);
          // 克隆模型
          const _newModel = newModel.clone();
          _newModel.position.copy(fixedVec3Point);
          // 插入模型
          this.group.add(_newModel);
          this.cropResourceTracker.track(newModel);
          this.cropObjects.push(_newModel);
        });
        counter++;
      }
    });
  }

  /**
   * 更改作物
   * TODO: 功能增强
   *  - 修改作物类型 - 通过url映射
   *  - 修改作物的种植密度 - 通过spacing
   * @param option 作物选项
   */
  changeCrop(option: { url: string; scale?: number }) {
    const { url, scale = 1 } = option;
    loadByUrl(url, scale).then((model) => {
      this._renderCrops(model);
    });
  }

  removeAllCrops() {
    this.cropObjects.forEach((crop) => {
      this.group.remove(crop);
    });
    this.cropObjects = [];
    this.cropResourceTracker.dispose();
  }

  /** @description 获取cropPlot的唯一id */
  getId(): string | number {
    return this.id;
  }

  destroy() {
    this.removeAllCrops();
    this.resourceTracker.dispose();
    this.cropStaffGroup.remove(this.group);
    this.resourceTracker = null;
    this.cropResourceTracker = null;
  }
}
